; =============================================================================
;A Entrada do Kernel

; 	O ponto de entrada do kernel é um pedaco de código que é executado PRIMEIRO,
; quando o bootloader chama seu kernel.
;	Este pedaço de código é quase sempre escrito em linguagem assembly por causa
; de algumas coisas, tal como criar uma nova pilha ou carregar uma nova GDT, IDT,
; ou registros de segmento, são coisas que você simplesmente não consegue no
; seu código C.
;	Em muitos kernels principiantes assim como outros kernels mais profissionais,
; mais grandes, vão por _todo do seu código assembly neste único arquivo, e por
; _todo o resto das fontes em vários arquivos fontes C.

;	Se você entende até mesmo uma pequena quantia de assembly, o código atual
; neste arquivo deve ser muito reto a frente. Como ao longo do código mostra,
; _todo este arquivo faz é carregar uma nova pilha de 8KByte, e então pular num
; loop infinito.
;	A pilha é uma pequena área da memória, mas é usada para guardar ou passar
; argumentos para funções em C. É também usada para segurar variáveis locais que
; você declara e usa dentro das suas funções. Qualquer outras variáveis globais
; são guardadas nas seções de dados e BSS. As linhas entre os blocos 'mboot' e
; 'stublet' criam uma assinatura especial que o GRUB usa para verificar que a
; saída binária que está para carregar é, de fato, um kernel.
;	Também não lute para compreender o cabeçalho de multiboot.



; Este eh o ponto de entrada do kernel.
; Podemos chamar o kmain aqui, ou podemos usa-lo para configurar a pilha
; ou outras coisas uteis, como por exemplo, configurar o GDT...
; Note que interrupcoes estao desabilitadas neste momento.
; Tratamento de interrupcoes eh pra depois!

; =========================================================
[BITS 32]
global start
start:
    mov esp, _sys_stack     ; Aponta a pilha para a sua nossa area de pilha
    jmp stublet

; Esta parte DEVE ser '4Byte' alinhada. Entao resolvemos isso usando 'ALIGN 4'
ALIGN 4
mboot:
	; Macros multiboot para fazer que as proximas linhas possam ser mais legiveis
    MULTIBOOT_PAGE_ALIGN	equ 1<<0 ; Load kernel and modules on a page boundary
    MULTIBOOT_MEMORY_INFO	equ 1<<1 ; Provide your kernel with memory info
    MULTIBOOT_AOUT_KLUDGE	equ 1<<16
    MULTIBOOT_HEADER_MAGIC	equ 0x1BADB002 ; Multiboot Magic value
    MULTIBOOT_HEADER_FLAGS	equ MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO | MULTIBOOT_AOUT_KLUDGE
    MULTIBOOT_CHECKSUM	equ -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)
    EXTERN code, bss, end

    ; Este eh o cabeçalho do multiboot GRUB. Uma assinatura de boot.
    dd MULTIBOOT_HEADER_MAGIC   ; GRUB will search for this value on each
    							; 4-byte boundary in your kernel file
    dd MULTIBOOT_HEADER_FLAGS   ; How GRUB should load your file / settings
    dd MULTIBOOT_CHECKSUM       ; To ensure that the above values are correct

    ; AOUT kludge - precisa ser endereços físicos.
    ; ANOTE ISSO: O script de linker preenche os dados para uns destes!
    dd mboot ; Make 'mboot' accessible from C.
    dd code  ; Start of the '.text' section.
    dd bss   ; Start of the .bss section.
    dd end   ; End of the last loadable section.
    dd start


; ========= CHAMADA DO KMAIN =========
; Este eh um laco sem fim.
; Chamamos o kmain antes disso...
stublet:
    extern kmain
    call kmain
    jmp $
; =====================================

[GLOBAL gdt_flush]    ; Allows the C code to call gdt_flush().



[GLOBAL idt_flush]    ; Allows the C code to call idt_flush().


;===================== GDT ====================
; Aqui configuramos os nossos novos 'segment registers'.
; Aqui vamos atribuir nossos novos registradores de segmento.
; Precisamos fazer algo especial, para definir o CS.
; Nós faremos o que é chamado de um salto longo (far jump)
; Um far jump é um salto que inclui um segmento assim como um offset
;  Este é declarado em C como 'extern void gdt_flush();'
global gdt_flush  ; Permite ao código C juntar este
gdt_flush:
    mov eax, [esp+4]  ; Get the pointer to the GDT, passed as a parameter.
    lgdt [eax]        ; Load the new GDT pointer

    mov ax, 0x10      ; 0x10 is the offset in the GDT to our data segment
    mov ds, ax        ; Load all data segment selectors
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax
    jmp 0x08:.flush   ; 0x08 is the offset to our code segment: Far jump!
.flush:
    ret

;================== TSS =========================
GLOBAL tss_flush    ; Allows our C code to call tss_flush().
tss_flush:
    mov ax, 0x2B      ; Load the index of our TSS structure - The index is
                      ; 0x28, as it is the 5th selector and each is 8 bytes
                      ; long, but we set the bottom two bits (making 0x2B)
                      ; so that it has an RPL of 3, not zero.
    ltr ax            ; Load 0x2B into the task state register.
    ret


;===================== IDT ========================
; Carrega o IDT definido em 'idtp' no processador
; Declarado em C como 'extern void idt_load();
global idt_load
idt_flush:
    mov eax, [esp+4]  ; Get the pointer to the IDT, passed as a parameter.
    lidt [eax]        ; Load the IDT pointer.
    ret

;===================== ISRs=========================
; Aqui adicionamos nossas Interrupt Service Routines (ISRs)
global isr0
global isr1
global isr2
global isr3
global isr4
global isr5
global isr6
global isr7
global isr8
global isr9
global isr10
global isr11
global isr12
global isr13
global isr14
global isr15
global isr16
global isr17
global isr18
global isr19
global isr20
global isr21
global isr22
global isr23
global isr24
global isr25
global isr26
global isr27
global isr28
global isr29
global isr30
global isr31

;  0: Divide By Zero Exception
isr0:
    cli
    push byte 0     ;Uma ponta normal de ISR retira uma cópia do código de erro
    				;para guardar o estado da pilha uniforme
    push byte 0
    jmp isr_common_stub

;  1: Debug Exception
isr1:
    cli
    push byte 0
    push byte 1
    jmp isr_common_stub

;  2: Non Maskable Interrupt Exception
isr2:
    cli
    push byte 0
    push byte 2
    jmp isr_common_stub

;  3: Int 3 Exception
isr3:
    cli
    push byte 0
    push byte 3
    jmp isr_common_stub

;  4: INTO Exception
isr4:
    cli
    push byte 0
    push byte 4
    jmp isr_common_stub

;  5: Out of Bounds Exception
isr5:
    cli
    push byte 0
    push byte 5
    jmp isr_common_stub

;  6: Invalid Opcode Exception
isr6:
    cli
    push byte 0
    push byte 6
    jmp isr_common_stub

;  7: Coprocessor Not Available Exception
isr7:
    cli
    push byte 0
    push byte 7
    jmp isr_common_stub

;----------------------------
;  8: Double Fault Exception (Com código de Erro!)
isr8:
    cli
    push byte 8     ; Note que nós NÃO puxamos um valor na pilha neste!
    				; Isso puxa um já! Use esta ponta para exceções
    				; Isso retira códigos de erro!
    jmp isr_common_stub

;  9: Coprocessor Segment Overrun Exception
isr9:
    cli
    push byte 0
    push byte 9
    jmp isr_common_stub

; 10: Bad TSS Exception (With Error Code!)
isr10:
    cli
    push byte 10
    jmp isr_common_stub

; 11: Segment Not Present Exception (With Error Code!)
isr11:
    cli
    push byte 11
    jmp isr_common_stub

; 12: Stack Fault Exception (With Error Code!)
isr12:
    cli
    push byte 12
    jmp isr_common_stub

; 13: General Protection Fault Exception (With Error Code!)
isr13:
    cli
    push byte 13
    jmp isr_common_stub

; 14: Page Fault Exception (With Error Code!)
isr14:
    cli
    push byte 14
    jmp isr_common_stub

; 15: Reserved Exception
isr15:
    cli
    push byte 0
    push byte 15
    jmp isr_common_stub

; 16: Floating Point Exception
isr16:
    cli
    push byte 0
    push byte 16
    jmp isr_common_stub

; 17: Alignment Check Exception
isr17:
    cli
    push byte 0
    push byte 17
    jmp isr_common_stub

; 18: Machine Check Exception
isr18:
    cli
    push byte 0
    push byte 18
    jmp isr_common_stub

; 19: Reserved
isr19:
    cli
    push byte 0
    push byte 19
    jmp isr_common_stub

; 20: Reserved
isr20:
    cli
    push byte 0
    push byte 20
    jmp isr_common_stub

; 21: Reserved
isr21:
    cli
    push byte 0
    push byte 21
    jmp isr_common_stub

; 22: Reserved
isr22:
    cli
    push byte 0
    push byte 22
    jmp isr_common_stub

; 23: Reserved
isr23:
    cli
    push byte 0
    push byte 23
    jmp isr_common_stub

; 24: Reserved
isr24:
    cli
    push byte 0
    push byte 24
    jmp isr_common_stub

; 25: Reserved
isr25:
    cli
    push byte 0
    push byte 25
    jmp isr_common_stub

; 26: Reserved
isr26:
    cli
    push byte 0
    push byte 26
    jmp isr_common_stub

; 27: Reserved
isr27:
    cli
    push byte 0
    push byte 27
    jmp isr_common_stub

; 28: Reserved
isr28:
    cli
    push byte 0
    push byte 28
    jmp isr_common_stub

; 29: Reserved
isr29:
    cli
    push byte 0
    push byte 29
    jmp isr_common_stub

; 30: Reserved
isr30:
    cli
    push byte 0
    push byte 30
    jmp isr_common_stub

; 31: Reserved
isr31:
    cli
    push byte 0
    push byte 31
    jmp isr_common_stub

; Nos chamamos uma funcao C aqui. Precisamos informar ao assembler que
; 'fault_handler' existe em outro arquivo
extern fault_handler

; Esste eh o nosso tronco comumISR. Ele salva o estado do processador,
; atribui os segmentos para modo kernel, chama o tratamento de falhas escrito
; em C, e finalmente restaura o estado da pilha.
isr_common_stub:
    pusha
    push ds
    push es
    push fs
    push gs
    mov ax, 0x10   ; Carrega o descritor de Segmento de Dados do Kernel!
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov eax, esp   ; Puxa nossa pilha
    push eax
    mov eax, fault_handler
    call eax      ; Uma chamada especial, preserva o registrador 'eip'
    pop eax
    pop gs
    pop fs
    pop es
    pop ds
    popa
    add esp, 8  ; Limpa o código de erro puxado e o número da ISR puxada
    iret        ; retira 5 coisas de uma vez: CS, EIP, EFLAGS, SS, e ESP!

; ==================== IRQs ===============
global irq0
global irq1
global irq2
global irq3
global irq4
global irq5
global irq6
global irq7
global irq8
global irq9
global irq10
global irq11
global irq12
global irq13
global irq14
global irq15

; 32: IRQ0
irq0:
    cli
    push byte 0   ; Note que este não puxa um código de erro para a pilha:
    			  ; Nós iremos puxar uma imitação de código de erro
    push byte 32
    jmp irq_common_stub

; 33: IRQ1
irq1:
    cli
    push byte 0
    push byte 33
    jmp irq_common_stub

; 34: IRQ2
irq2:
    cli
    push byte 0
    push byte 34
    jmp irq_common_stub

; 35: IRQ3
irq3:
    cli
    push byte 0
    push byte 35
    jmp irq_common_stub

; 36: IRQ4
irq4:
    cli
    push byte 0
    push byte 36
    jmp irq_common_stub

; 37: IRQ5
irq5:
    cli
    push byte 0
    push byte 37
    jmp irq_common_stub

; 38: IRQ6
irq6:
    cli
    push byte 0
    push byte 38
    jmp irq_common_stub

; 39: IRQ7
irq7:
    cli
    push byte 0
    push byte 39
    jmp irq_common_stub

; 40: IRQ8
irq8:
    cli
    push byte 0
    push byte 40
    jmp irq_common_stub

; 41: IRQ9
irq9:
    cli
    push byte 0
    push byte 41
    jmp irq_common_stub

; 42: IRQ10
irq10:
    cli
    push byte 0
    push byte 42
    jmp irq_common_stub

; 43: IRQ11
irq11:
    cli
    push byte 0
    push byte 43
    jmp irq_common_stub

; 44: IRQ12
irq12:
    cli
    push byte 0
    push byte 44
    jmp irq_common_stub

; 45: IRQ13
irq13:
    cli
    push byte 0
    push byte 45
    jmp irq_common_stub

; 46: IRQ14
irq14:
    cli
    push byte 0
    push byte 46
    jmp irq_common_stub

; 47: IRQ15
irq15:
    cli
    push byte 0
    push byte 47
    jmp irq_common_stub

extern irq_handler

; Esta é uma parte que nós temos criado para IRQ baseado em ISRs. Estes chamam
; '_irq_handler' em nosso código C. Nós devemos criar este em 'irq.c'
irq_common_stub:
    pusha
    push ds
    push es
    push fs
    push gs

    mov ax, 0x10
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov eax, esp

    push eax
    mov eax, irq_handler
    call eax
    pop eax

    pop gs
    pop fs
    pop es
    pop ds
    popa
    add esp, 8
    iret

; Esta eh a definicao da sua secao de BSS.
; Neste momento vamos usar apenas para armazenar a pilha.
; Lembre-se que uma pilha atualmente cresce para baixo,
; por isso nos declaramos o tamanho dos dados antes de declarar
; o identificador '_sys_stack'
SECTION .bss
    resb 8192               ; Reserva 8KBytes de memoria
_sys_stack:

;==============================================================================
;================= O Script de Linker ==================

;	O Linker é a ferramenta que toma todos os seus arquivos compilados e
; junta-os então em um único arquivo binário. Um arquivo binário pode ter vários
; formatos: Flat, AOUT, COFF, PE, e ELF são os mais comuns.
;	O linker que nós escolhemos em nosso conjunto de ferramentas foi o linker LD.
; Este é um ótimo linker multi-propósito com um extenso conjunto de recursos.
; Há versões existentes do LD que podem fazer um binário sair em qualquer formato
; que você deseja. Apesar do formato que você escolha, sempre haverá 3 'seções'
; ('sections') no arquivo final. 'Texto' ('Text') ou 'Código' ('Code') é o
; executável em sí. A seção de 'Dados' ('Data' section) é para valores
; inicializados do seu código, tal como você declara um variável e lhe atribui o
; valor 5. O valor de 5 poderá ser armazenado na seção de 'Dados' ('Data' section).
; A última seção é chamada seção 'BSS' ('BSS' section). O 'BSS' consiste de dados
; não inicializados; este guarda quaisquer arranjos (arrays) que você não
; atribuiu quaisquer valores a eles, por exemplo. 'BSS' é uma seção virtual:
; Esta não existe na imagem binária, mas este existem na memória quando seu
; binário é carregado.

;	O que se segue abaixo é um Script para o Linker LD. Há 3 palavras-chaves
; maiores que podemos retirar deste script de linker para ser analisados:
; OUTPUT_FORMAT vai dizer ao LD qual imagem binária vai ser criada. Para fazer
; isto simples, nós persistiremos para usar uma imagem "binária" ("binary").
; ENTRY vai dizer ao linker qual arquivo objeto a ser juntado vai ser o primeiro
; arquivo da lista. Nós desejaremos a versão compilada de 'start.asm' chamada
; 'start.o' para ser o primeiro arquivo objeto a ser juntado, porque este é o
; ponto de entrada do kernel. A próxima linha é 'phys'. Está não é uma
; palavra-chave, mas uma variável para ser usada no script de linker. Neste
; caso, nós vamos usa-la como um ponto para um endereço de memória: um ponteiro
; para 1MByte, onde nosso binário estará sendo carregado e rodará. A 3ª
; palavra-chave é SECTIONS. Se você estudar este script de linker, você vai ver
; que se define 3 seções principais: '.text', '.data', e '.bss'.
; Há 3 variáveis definidas também: 'code', 'data', 'bss', e 'end'. Não fique
; confuso com isto: as 3 variáveis que você vê são atualmente variáveis que
; estão em nosso arquivo de inicialização, start.asm. ALIGN(4096) assegura que
; cada seção inicie em um limite de 4096byte. Neste caso, asseguraremos que cada
; seção começará em uma 'página' em separado da memória.

; === Conteudo de linker.ld

;OUTPUT_FORMAT("binary")
;ENTRY(start)
;phys = 0x00100000;
;SECTIONS
;{
;  .text phys : AT(phys) {
;    code = .;
;    *(.text)
;    *(.rodata)
;    . = ALIGN(4096);
;  }
;  .data : AT(phys + (data - code))
;  {
;    data = .;
;   *(.data)
;    . = ALIGN(4096);
;  }
;  .bss : AT(phys + (bss - code))
;  {
;    bss = .;
;    *(.bss)
;    . = ALIGN(4096);
;  }
;  end = .;
;}
